home *** CD-ROM | disk | FTP | other *** search
- /* PlayTrackInfoThang.c */
- /*****************************************************************************/
- /* */
- /* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
- /* Copyright (C) 1994 Thomas R. Lawrence */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
- /* */
- /*****************************************************************************/
-
- #include "MiscInfo.h"
- #include "Audit.h"
- #include "Debug.h"
- #include "Definitions.h"
-
- #include "PlayTrackInfoThang.h"
- #include "Array.h"
- #include "Memory.h"
- #include "OscBankPlayer.h"
- #include "TrackObject.h"
- #include "IncrementalParameterUpdator.h"
- #include "Fractions.h"
- #include "FrameObject.h"
- #include "DeterminedNoteStructure.h"
- #include "NoteObject.h"
- #include "ErrorDaemon.h"
- #include "TrackEffectGenerator.h"
- #include "InstrumentStructure.h"
-
-
- typedef struct FrozenNoteConsCell
- {
- /* list link */
- struct FrozenNoteConsCell* Next;
-
- /* good information to be used for tied notes */
- FrozenNoteRec* FrozenNote;
-
- /* tie target for tied notes, NIL if there is no tie */
- struct NoteObjectRec* TieTarget;
-
- /* when this note should take over (in absolute envelope ticks) */
- long ContinuationTime;
- } FrozenNoteConsCell;
-
-
- typedef struct OscBankConsCell
- {
- /* link for listing */
- struct OscBankConsCell* Next;
- struct OscBankConsCell* Previous;
-
- /* the oscillator bank */
- OscStateBankRec* OscBank;
-
- /* still active flag */
- MyBoolean StillActive;
-
- /* tie target for tied notes, NIL if there is no tie */
- struct NoteObjectRec* TieTarget;
-
- /* this is the start time of the note, used for ordering the scanning gap list */
- long StartTime;
-
- /* this is an ordered list of frozen notes ready for tie continuation */
- /* this is in sorted ascending order of TieContinuationList->ContinuationTime */
- FrozenNoteConsCell* TieContinuationList;
- } OscBankConsCell;
-
-
- struct PlayTrackInfoRec
- {
- /* frame source */
- TrackObjectRec* TrackObject;
- /* total number of frames in the track object */
- long TotalNumberOfFrames;
- /* index into frame array. */
- long FrameArrayIndex;
- /* number of cycles until the next frame should be processed. this is in */
- /* units of duration update cycles. when this runs out, then another frame */
- /* should be processed. */
- long NextFrameCountDown;
-
- /* list of notes that have been scheduled but haven't been executed yet. */
- /* it contains objects of type OscBankConsCell. */
- OscBankConsCell* ScanningGapListHead; /* of OscBankConsCell's */
- OscBankConsCell* ScanningGapListTail; /* of OscBankConsCell's */
- /* this is the current duration update index for the scanning gap list */
- long ExecutionIndex;
- /* this is the index of the first element in the scanning gap list. when */
- /* the execution index hits or exceeds this value, the first osc bank is popped */
- /* from the scanning gap list and executed. it is only valid when */
- /* ScanningGapLength is greater than zero (i.e. ScanningGapList is non-empty) */
-
- /* this object keeps track of the current value of all parameters, updates them */
- /* as time passes, and evaluates commands passed in from here. the state of */
- /* this object reflects the state at the front of scanning gap, since notes */
- /* are frozen immediately upon entering scanning gap. */
- IncrParamUpdateRec* ParameterController;
-
- /* this is the template used for creating oscillator banks */
- OscBankTemplateRec* OscillatorBankTemplate;
-
- /* this is a list of all currently executing oscillator banks. there is one */
- /* entry for each note that is currently being played. */
- OscBankConsCell* ExecutingOscillatorBanks;
-
- /* playback control parameters */
- float EnvelopeUpdateRate;
- float OverallVolumeScaling;
-
- /* area for generating samples into, and applying track effects */
- largefixedsigned* LocalSampleArray;
- long LocalSampleArrayLength;
-
- /* track effects */
- TrackEffectGenRec* EffectGenerator;
-
- /* score effects. we don't actually apply this, but we have it so that */
- /* we can send it commands. it is a reference to a shared object. */
- TrackEffectGenRec* ScoreEffectGenerator;
-
- /* flag remembering if we are using stereo */
- MyBoolean UsingStereo;
- };
-
-
- static OscBankConsCell* OscBankConsCellFreeList = NIL;
- struct FrozenNoteConsCell* FrozenNoteConsCellFreeList = NIL;
-
-
- /* dispose of cached track data structures */
- void FlushPlayTrackInfo(void)
- {
- while (OscBankConsCellFreeList != NIL)
- {
- OscBankConsCell* Temp;
-
- Temp = OscBankConsCellFreeList;
- OscBankConsCellFreeList = OscBankConsCellFreeList->Next;
- ReleasePtr((char*)Temp);
- }
-
- while (FrozenNoteConsCellFreeList != NIL)
- {
- FrozenNoteConsCell* Temp;
-
- Temp = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = FrozenNoteConsCellFreeList->Next;
- ReleasePtr((char*)Temp);
- }
- }
-
-
- /* create a new track play info thing and set a bunch of parameters. this also */
- /* builds the internal representations for instruments & oscillators for this track. */
- PlayTrackInfoRec* NewPlayTrackInfo(struct TrackObjectRec* TheTrack,
- struct InstrumentRec* InstrumentSpecification,
- MyBoolean StereoFlag, LargeBCDType OverallVolumeScalingReciprocal,
- long SamplingRate, float EnvelopeRate, MyBoolean TimeInterp,
- MyBoolean WaveInterp, struct TempoControlRec* TempoControl,
- long ScanningGapWidthInEnvelopeTicks,
- struct ErrorDaemonRec* ErrorDaemon,
- struct MainWindowRec* MainWindow,
- struct TrackEffectGenRec* ScoreEffectGenerator)
- {
- PlayTrackInfoRec* TrackInfo;
-
- CheckPtrExistence(TheTrack);
- CheckPtrExistence(InstrumentSpecification);
- CheckPtrExistence(TempoControl);
- CheckPtrExistence(ErrorDaemon);
- CheckPtrExistence(ScoreEffectGenerator);
- CheckPtrExistence(MainWindow);
-
- TrackInfo = (PlayTrackInfoRec*)AllocPtrCanFail(sizeof(PlayTrackInfoRec),
- "PlayTrackInfoRec");
- if (TrackInfo == NIL)
- {
- FailurePoint1:
- return NIL;
- }
-
- /* score effect generator */
- TrackInfo->ScoreEffectGenerator = ScoreEffectGenerator;
-
- /* frame source */
- TrackInfo->TrackObject = TheTrack;
-
- /* total number of frames in the track object */
- TrackInfo->TotalNumberOfFrames = TrackObjectGetNumFrames(TheTrack);
-
- /* index into frame array. */
- TrackInfo->FrameArrayIndex = 0;
-
- /* number of cycles until the next frame should be processed. this is in */
- /* units of duration update cycles. when this runs out, then another frame */
- /* should be processed. */
- TrackInfo->NextFrameCountDown = 0;
-
- /* list of notes that have been scheduled but haven't been executed yet. */
- /* it contains objects of type OscStateBankRec. */
- TrackInfo->ScanningGapListHead = NIL;
- TrackInfo->ScanningGapListTail = NIL;
-
- /* this is the current envelope update index for removing things from the */
- /* scanning gap list (i.e. the back edge of the scanning gap) */
- /* by setting this negative, we cause the scanning gap to open. */
- TrackInfo->ExecutionIndex = - ScanningGapWidthInEnvelopeTicks;
-
- /* this object keeps track of the current value of all parameters, updates them */
- /* as time passes, and evaluates commands passed in from here. */
- TrackInfo->ParameterController = NewInitializedParamUpdator(TheTrack,TempoControl);
- if (TrackInfo->ParameterController == NIL)
- {
- FailurePoint2:
- ReleasePtr((char*)TrackInfo);
- goto FailurePoint1;
- }
-
- /* this is the template used for creating oscillator banks */
- TrackInfo->OscillatorBankTemplate = NewOscBankTemplate(InstrumentSpecification,
- StereoFlag,OverallVolumeScalingReciprocal,SamplingRate,EnvelopeRate,
- TimeInterp,WaveInterp,TrackInfo->ParameterController,ErrorDaemon);
- if (TrackInfo->OscillatorBankTemplate == NIL)
- {
- FailurePoint3:
- DisposeParamUpdator(TrackInfo->ParameterController);
- goto FailurePoint2;
- }
-
- /* allocate new sample array thing */
- TrackInfo->LocalSampleArrayLength = 1000;
- TrackInfo->LocalSampleArray = (largefixedsigned*)AllocPtrCanFail(
- TrackInfo->LocalSampleArrayLength * sizeof(largefixedsigned),"LocalSampleArray");
- if (TrackInfo->LocalSampleArray == NIL)
- {
- FailurePoint4:
- DisposeOscBankTemplate(TrackInfo->OscillatorBankTemplate);
- goto FailurePoint3;
- }
-
- /* set up track effects */
- TrackInfo->EffectGenerator = NewTrackEffectGenerator(GetInstrumentEffectSpecList(
- InstrumentSpecification),SamplingRate,StereoFlag,LargeBCD2Single(
- OverallVolumeScalingReciprocal),MainWindow,ScanningGapWidthInEnvelopeTicks);
- if (TrackInfo->EffectGenerator == NIL)
- {
- FailurePoint5:
- ReleasePtr((char*)TrackInfo->LocalSampleArray);
- goto FailurePoint4;
- }
-
- /* remember stereo status */
- TrackInfo->UsingStereo = StereoFlag;
-
- /* this is a list of all currently executing oscillator banks. there is one */
- /* entry for each note that is currently being played. */
- TrackInfo->ExecutingOscillatorBanks = NIL;
-
- /* playback control parameters */
- TrackInfo->EnvelopeUpdateRate = EnvelopeRate;
- TrackInfo->OverallVolumeScaling = (float)1
- / LargeBCD2Double(OverallVolumeScalingReciprocal);
-
- return TrackInfo;
- }
-
-
- static void DisposeOscBankList(OscBankConsCell* List)
- {
- while (List != NIL)
- {
- OscBankConsCell* Temp;
-
- /* delink */
- Temp = List;
- List = List->Next;
- /* dispose members */
- DisposeOscStateBank(Temp->OscBank);
- while (Temp->TieContinuationList != NIL)
- {
- FrozenNoteConsCell* ColdTemp;
-
- ColdTemp = Temp->TieContinuationList;
- Temp->TieContinuationList = Temp->TieContinuationList->Next;
- DisposeFrozenNote(ColdTemp->FrozenNote);
- ColdTemp->Next = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = ColdTemp;
- }
- /* stick on free list */
- Temp->Next = OscBankConsCellFreeList;
- EXECUTE(Temp->Previous = (OscBankConsCell*)0x81818181;)
- OscBankConsCellFreeList = Temp;
- }
- }
-
-
- /* dump a track play info thing and all the stuff in it */
- void DisposePlayTrackInfo(PlayTrackInfoRec* TrackInfo)
- {
- CheckPtrExistence(TrackInfo);
- DisposeTrackEffectGenerator(TrackInfo->EffectGenerator);
- ReleasePtr((char*)TrackInfo->LocalSampleArray);
- DisposeOscBankList(TrackInfo->ScanningGapListHead);
- DisposeOscBankList(TrackInfo->ExecutingOscillatorBanks);
- DisposeOscBankTemplate(TrackInfo->OscillatorBankTemplate);
- DisposeParamUpdator(TrackInfo->ParameterController);
- ReleasePtr((char*)TrackInfo);
- }
-
-
- /* cue track forward to the specified point. returns False if it fails */
- MyBoolean CuePlayTrackInfoToPoint(PlayTrackInfoRec* TrackInfo,
- struct FractionRec* StartTime)
- {
- long AdvancementCounter;
-
- CheckPtrExistence(TrackInfo);
-
- /* convert whole-note fraction into duration ticks */
- ERROR(StartTime->Denominator != DURATIONUPDATECLOCKRESOLUTION,PRERR(AllowResume,
- "CuePlayTrackInfoToPoint: start time denominator has bad value"));
- AdvancementCounter = StartTime->Integer * StartTime->Denominator
- + StartTime->Fraction;
-
- /* search for the proper point to start playing */
- while ((AdvancementCounter > 0)
- && (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames))
- {
- FrameObjectRec* Frame; /* I've been framed! */
- FractionRec Duration;
- long TicksInFrame;
-
- /* get the frame */
- Frame = TrackObjectGetFrame(TrackInfo->TrackObject,TrackInfo->FrameArrayIndex);
- CheckPtrExistence(Frame);
- /* how long is it? */
- DurationOfFrame(Frame,&Duration);
- ERROR(Duration.Denominator != DURATIONUPDATECLOCKRESOLUTION,PRERR(AllowResume,
- "CuePlayTrackInfoToPoint: start time denominator has odd value"));
- TicksInFrame = Duration.Integer * Duration.Denominator + Duration.Fraction;
- /* decrement our lead-in counter */
- AdvancementCounter -= TicksInFrame;
- /* advance our pointer */
- TrackInfo->FrameArrayIndex += 1;
- }
-
- /* AdvancementCounter is either 0 or negative. if it's negative, then we */
- /* need to note that we must delay some before getting the next note */
- TrackInfo->NextFrameCountDown = - AdvancementCounter;
-
- return True;
- }
-
-
- /* check to see if the track has finished and can be dropped. */
- MyBoolean PlayTrackIsItStillActive(PlayTrackInfoRec* TrackInfo)
- {
- OscBankConsCell* OscBankScan;
-
- CheckPtrExistence(TrackInfo);
-
- /* in order to still be active, the following conditions must be satisfied: */
- /* - there are still active oscillators. */
- /* - there are still oscillators in the scanning gap list */
- /* - there are still notes which haven't been scanned yet */
-
- if (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames)
- {
- return True;
- }
-
- if (TrackInfo->ScanningGapListHead != NIL)
- {
- return True;
- }
-
- OscBankScan = TrackInfo->ExecutingOscillatorBanks;
- while (OscBankScan != NIL)
- {
- if (OscBankScan->StillActive)
- {
- return True;
- }
- OscBankScan = OscBankScan->Next;
- }
-
- return False;
- }
-
-
- /* auxilliary routine which searches a list for a tie source and installs the note */
- /* in the list if necessary. True is returned if it is installed. */
- static MyBoolean SearchForTieSource(OscBankConsCell* OscBankScan,
- struct NoteObjectRec* Note, long ScanningGapFrontInEnvelopeTicks,
- PlayTrackInfoRec* TrackInfo, float EnvelopeTicksPerDurationTick)
- {
- while (OscBankScan != NIL)
- {
- FrozenNoteConsCell* PlaceToPut;
- long StartAdjust;
-
- PlaceToPut = NIL;
- if (OscBankScan->TieTarget == Note)
- {
- /* found it */
- CreateTieContinuationPoint:
- if (FrozenNoteConsCellFreeList != NIL)
- {
- PlaceToPut = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = FrozenNoteConsCellFreeList->Next;
- }
- else
- {
- PlaceToPut = (FrozenNoteConsCell*)AllocPtrCanFail(
- sizeof(FrozenNoteConsCell),"FrozenNoteConsCell");
- if (PlaceToPut == NIL)
- {
- return False;
- }
- }
- }
- else
- {
- FrozenNoteConsCell* TargScan;
- FrozenNoteConsCell* TargLag;
-
- /* search tie target things */
- TargScan = OscBankScan->TieContinuationList;
- TargLag = NIL;
- while (TargScan != NIL)
- {
- if (TargScan->TieTarget == Note)
- {
- /* found one */
- goto CreateTieContinuationPoint;
- }
- TargLag = TargScan;
- TargScan = TargScan->Next;
- }
- }
- if (PlaceToPut != NIL)
- {
- FrozenNoteConsCell* InsertScan;
- FrozenNoteConsCell* InsertLag;
-
- /* fill in the fields */
- PlaceToPut->FrozenNote = FixNoteParameters(
- TrackInfo->ParameterController,Note,&StartAdjust,
- TrackInfo->OverallVolumeScaling,EnvelopeTicksPerDurationTick);
- if (PlaceToPut->FrozenNote == NIL)
- {
- PlaceToPut->Next = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = PlaceToPut;
- return False;
- }
- PlaceToPut->ContinuationTime = StartAdjust + ScanningGapFrontInEnvelopeTicks;
- PlaceToPut->TieTarget = GetNoteTieTarget(Note);
- /* insert it into the proper place */
- InsertScan = OscBankScan->TieContinuationList;
- InsertLag = NIL;
- while ((InsertScan != NIL) && (InsertScan->ContinuationTime
- <= PlaceToPut->ContinuationTime))
- {
- InsertLag = InsertScan;
- InsertScan = InsertScan->Next;
- }
- PlaceToPut->Next = InsertScan;
- if (InsertLag == NIL)
- {
- OscBankScan->TieContinuationList = PlaceToPut;
- }
- else
- {
- InsertLag->Next = PlaceToPut;
- }
- /* we found it! */
- return True;
- }
- OscBankScan = OscBankScan->Next;
- }
- return False;
- }
-
-
- /* perform one envelope clock cycle update. if UpdateEnvelopes is true, then */
- /* wave data should be generated and envelopes should be updated, otherwise only */
- /* note scheduling should be performed. */
- MyBoolean PlayTrackUpdate(PlayTrackInfoRec* TrackInfo,
- MyBoolean UpdateEnvelopes, long NumDurationTicks,
- long NumFrames, largefixedsigned* OutputData,
- float EnvelopeTicksPerDurationTick,
- long ScanningGapFrontInEnvelopeTicks)
- {
- OscBankConsCell* OscBankScan;
- OscBankConsCell* OscBankLag;
-
- CheckPtrExistence(TrackInfo);
-
- /* schedule any notes out of the track list into the scanning gap */
- while ((TrackInfo->NextFrameCountDown <= 0)
- && (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames))
- {
- FrameObjectRec* Frame;
-
- /* schedule a frame */
- Frame = TrackObjectGetFrame(TrackInfo->TrackObject,TrackInfo->FrameArrayIndex);
- CheckPtrExistence(Frame);
- TrackInfo->FrameArrayIndex += 1;
- if (IsThisACommandFrame(Frame))
- {
- NoteObjectRec* Command;
-
- /* it's a command */
- Command = GetNoteFromFrame(Frame,0);
- /* if it's an effect command, then send it to the effect processor */
- /* otherwise we handle it */
- switch (GetCommandOpcode(Command))
- {
- default:
- ExecuteParamCommandFrame(TrackInfo->ParameterController,Command);
- break;
- case eCmdSetEffectParam1:
- case eCmdIncEffectParam1:
- case eCmdSweepEffectParamAbs1:
- case eCmdSweepEffectParamRel1:
- case eCmdSetEffectParam2:
- case eCmdIncEffectParam2:
- case eCmdSweepEffectParamAbs2:
- case eCmdSweepEffectParamRel2:
- case eCmdSetEffectParam3:
- case eCmdIncEffectParam3:
- case eCmdSweepEffectParamAbs3:
- case eCmdSweepEffectParamRel3:
- case eCmdSetEffectParam4:
- case eCmdIncEffectParam4:
- case eCmdSweepEffectParamAbs4:
- case eCmdSweepEffectParamRel4:
- case eCmdTrackEffectEnable:
- if (!TrackEffectHandleCommand(TrackInfo->EffectGenerator,
- Command,ScanningGapFrontInEnvelopeTicks))
- {
- return False;
- }
- break;
- case eCmdSetScoreEffectParam1:
- case eCmdIncScoreEffectParam1:
- case eCmdSweepScoreEffectParamAbs1:
- case eCmdSweepScoreEffectParamRel1:
- case eCmdSetScoreEffectParam2:
- case eCmdIncScoreEffectParam2:
- case eCmdSweepScoreEffectParamAbs2:
- case eCmdSweepScoreEffectParamRel2:
- case eCmdSetScoreEffectParam3:
- case eCmdIncScoreEffectParam3:
- case eCmdSweepScoreEffectParamAbs3:
- case eCmdSweepScoreEffectParamRel3:
- case eCmdSetScoreEffectParam4:
- case eCmdIncScoreEffectParam4:
- case eCmdSweepScoreEffectParamAbs4:
- case eCmdSweepScoreEffectParamRel4:
- if (!TrackEffectHandleCommand(TrackInfo->ScoreEffectGenerator,
- Command,ScanningGapFrontInEnvelopeTicks))
- {
- return False;
- }
- break;
- }
- }
- else
- {
- long FrameLimit;
- long FrameScan;
- FractionRec FrameDuration;
-
- /* increment the frame counter */
- DurationOfFrame(Frame,&FrameDuration);
- ERROR(DURATIONUPDATECLOCKRESOLUTION != FrameDuration.Denominator,
- PRERR(AllowResume,"PlayTrackUpdate: strange denominator in frame duration"));
- TrackInfo->NextFrameCountDown += FrameDuration.Denominator
- * FrameDuration.Integer + FrameDuration.Fraction;
-
- /* it's a real note */
- FrameLimit = NumNotesInFrame(Frame);
- for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
- {
- struct NoteObjectRec* Note;
- OscBankConsCell* NewOscBank;
- OscBankConsCell* LinkingScan;
-
- Note = GetNoteFromFrame(Frame,FrameScan);
- CheckPtrExistence(Note);
- if (GetNoteIsItARest(Note))
- {
- /* just ignore rests */
- goto EndFrameScanPoint; /* this should really be a conditional */
- }
-
- /* first, scan the oscillator list to see if this is a note that */
- /* someone wants to tie to. if it is, then build a frozen note */
- /* structure and add it to the object. otherwise, build an */
- /* oscillator bank and add it to the scanning gap. */
-
- /* search all oscillator banks to see if it's a tie target */
- if (SearchForTieSource(TrackInfo->ExecutingOscillatorBanks,Note,
- ScanningGapFrontInEnvelopeTicks,TrackInfo,
- EnvelopeTicksPerDurationTick))
- {
- goto EndFrameScanPoint;
- }
- if (SearchForTieSource(TrackInfo->ScanningGapListHead,Note,
- ScanningGapFrontInEnvelopeTicks,TrackInfo,
- EnvelopeTicksPerDurationTick))
- {
- goto EndFrameScanPoint;
- }
-
- /* if we got here, then it's not a tie target */
- if (OscBankConsCellFreeList != NIL)
- {
- NewOscBank = OscBankConsCellFreeList;
- OscBankConsCellFreeList = OscBankConsCellFreeList->Next;
- }
- else
- {
- NewOscBank = (OscBankConsCell*)AllocPtrCanFail(
- sizeof(OscBankConsCell),"OscBankConsCell");
- if (NewOscBank == NIL)
- {
- return False;
- }
- }
- NewOscBank->OscBank = NewOscBankState(TrackInfo->OscillatorBankTemplate,
- &(NewOscBank->StartTime),Note,EnvelopeTicksPerDurationTick);
- if (NewOscBank->OscBank == NIL)
- {
- NewOscBank->Next = OscBankConsCellFreeList;
- OscBankConsCellFreeList = NewOscBank;
- return False;
- }
- NewOscBank->StartTime += ScanningGapFrontInEnvelopeTicks; /* fix up start time */
- NewOscBank->StillActive = True;
- NewOscBank->TieTarget = GetOscStateTieTarget(NewOscBank->OscBank);
- NewOscBank->TieContinuationList = NIL;
- /* link it in */
- LinkingScan = TrackInfo->ScanningGapListTail;
- while ((LinkingScan != NIL)
- && (LinkingScan->StartTime > NewOscBank->StartTime))
- {
- LinkingScan = LinkingScan->Previous;
- }
- if (LinkingScan == NIL)
- {
- NewOscBank->Previous = NIL;
- NewOscBank->Next = TrackInfo->ScanningGapListHead;
- if (TrackInfo->ScanningGapListHead != NIL)
- {
- TrackInfo->ScanningGapListHead->Previous = NewOscBank;
- }
- TrackInfo->ScanningGapListHead = NewOscBank;
- if (TrackInfo->ScanningGapListTail == NIL)
- {
- /* this happens if there were no nodes at all */
- TrackInfo->ScanningGapListTail = NewOscBank;
- }
- }
- else
- {
- /* insert after Scan */
- NewOscBank->Previous = LinkingScan;
- NewOscBank->Next = LinkingScan->Next;
- LinkingScan->Next = NewOscBank;
- if (LinkingScan == TrackInfo->ScanningGapListTail)
- {
- /* this happens if Scan was the last element; */
- /* NewNode becomes last element */
- TrackInfo->ScanningGapListTail = NewOscBank;
- }
- }
-
- EndFrameScanPoint:
- ;
- }
- }
- }
-
- /* do timing update */
- TrackInfo->NextFrameCountDown -= NumDurationTicks;
-
- /* update global parameters */
- ExecuteParamUpdate(TrackInfo->ParameterController,NumDurationTicks);
-
- /* generate waveforms, update envelope generators & notes, etc. */
- /* there are 3 stages to stuff */
- /* 1. when UpdateEnvelopes is false, the only action is to queue up notes */
- /* to be played. this opens the scanning gap. */
- /* 2. when UpdateEnvelopes is true, but ExecutionIndex is still less than zero, */
- /* notes that start before the start of the song (due to pre-origin segments */
- /* or other things) are started and processed. */
- /* 3. Eventually, ExecutionIndex will be >= 0, and the official start of the */
- /* song will have been passed. */
- if (UpdateEnvelopes)
- {
- /* make sure buffer is big enough, and zero it out */
- if (TrackInfo->UsingStereo)
- {
- long Scan;
-
- if (NumFrames * 2 > TrackInfo->LocalSampleArrayLength)
- {
- largefixedsigned* TempArray;
-
- TempArray = (largefixedsigned*)ResizePtr(
- (char*)TrackInfo->LocalSampleArray,NumFrames * 2
- * sizeof(largefixedsigned));
- if (TempArray == NIL)
- {
- return False;
- }
- TrackInfo->LocalSampleArray = TempArray;
- TrackInfo->LocalSampleArrayLength = NumFrames * 2;
- }
- /* initialize array */
- for (Scan = 0; Scan < NumFrames * 2; Scan += 1)
- {
- PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
- sizeof((TrackInfo->LocalSampleArray[Scan])));
- TrackInfo->LocalSampleArray[Scan] = 0;
- }
- }
- else
- {
- long Scan;
-
- if (NumFrames > TrackInfo->LocalSampleArrayLength)
- {
- largefixedsigned* TempArray;
-
- TempArray = (largefixedsigned*)ResizePtr(
- (char*)TrackInfo->LocalSampleArray,NumFrames
- * sizeof(largefixedsigned));
- if (TempArray == NIL)
- {
- return False;
- }
- TrackInfo->LocalSampleArray = TempArray;
- TrackInfo->LocalSampleArrayLength = NumFrames;
- }
- /* initialize array */
- for (Scan = 0; Scan < NumFrames; Scan += 1)
- {
- PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
- sizeof((TrackInfo->LocalSampleArray[Scan])));
- TrackInfo->LocalSampleArray[Scan] = 0;
- }
- }
-
- /* see if any ties have to be tripped */
- OscBankScan = TrackInfo->ExecutingOscillatorBanks;
- while (OscBankScan != NIL)
- {
- while ((OscBankScan->TieContinuationList != NIL)
- && (OscBankScan->TieContinuationList->ContinuationTime
- <= TrackInfo->ExecutionIndex))
- {
- FrozenNoteConsCell* Temp;
-
- /* yow, let's do this one! */
- /* delink */
- Temp = OscBankScan->TieContinuationList;
- OscBankScan->TieContinuationList
- = OscBankScan->TieContinuationList->Next;
- /* execute */
- if (!ResetOscBankState(OscBankScan->OscBank,Temp->FrozenNote,
- EnvelopeTicksPerDurationTick))
- {
- return False; /* oh, no! */
- }
- OscBankScan->TieTarget = Temp->TieTarget;
- /* clean up */
- DisposeFrozenNote(Temp->FrozenNote);
- Temp->Next = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = Temp;
- }
- OscBankScan = OscBankScan->Next;
- }
-
- /* schedule a note from the scanning gap */
- while ((TrackInfo->ScanningGapListHead != NIL)
- && (TrackInfo->ScanningGapListHead->StartTime <= TrackInfo->ExecutionIndex))
- {
- OscBankConsCell* NewConsCell;
-
- /* yup, schedule the oscillator */
- /* [technically, the start time should never be strictly less than the */
- /* execution index (only equal), but it can occur if the user specifies */
- /* a scanning gap that's two narrow.] */
- NewConsCell = TrackInfo->ScanningGapListHead;
- TrackInfo->ScanningGapListHead = TrackInfo->ScanningGapListHead->Next;
- if (TrackInfo->ScanningGapListHead != NIL)
- {
- TrackInfo->ScanningGapListHead->Previous = NIL;
- }
- else
- {
- TrackInfo->ScanningGapListTail = NIL;
- }
- /* link it in */
- NewConsCell->Next = TrackInfo->ExecutingOscillatorBanks;
- TrackInfo->ExecutingOscillatorBanks = NewConsCell;
- EXECUTE(NewConsCell->Previous = (OscBankConsCell*)0x81818181;)
- }
-
- /* let track effect generator schedule commands */
- TrackEffectProcessQueuedCommands(TrackInfo->EffectGenerator);
-
- /* increment our scanning gap back edge clock, after scheduling commands */
- /* (this way, commands are scheduled on the beginning of the clock they */
- /* should occur on). */
- TrackInfo->ExecutionIndex += 1;
-
- /* wave generator and envelope update loop */
- OscBankScan = TrackInfo->ExecutingOscillatorBanks;
- OscBankLag = NIL;
- while (OscBankScan != NIL)
- {
- OscBankScan->StillActive = !UpdateOscStateBank(OscBankScan->OscBank,
- NumFrames,TrackInfo->LocalSampleArray);
- if (!OscBankScan->StillActive && (OscBankScan->TieContinuationList == NIL))
- {
- OscBankConsCell* Temp;
-
- /* not tied to anybody, so kill it */
- DisposeOscStateBank(OscBankScan->OscBank);
- if (OscBankLag == NIL)
- {
- TrackInfo->ExecutingOscillatorBanks = OscBankScan->Next;
- }
- else
- {
- OscBankLag->Next = OscBankScan->Next;
- }
- Temp = OscBankScan;
- OscBankScan = OscBankScan->Next;
- Temp->Next = OscBankConsCellFreeList;
- OscBankConsCellFreeList = Temp;
- }
- else
- {
- OscBankLag = OscBankScan;
- OscBankScan = OscBankScan->Next;
- }
- }
-
- /* apply effects to local array */
- ApplyTrackEffectGenerator(TrackInfo->EffectGenerator,
- TrackInfo->LocalSampleArray,NumFrames);
-
- /* copy data from local array to global array */
- if (TrackInfo->UsingStereo)
- {
- long Scan;
-
- for (Scan = 0; Scan < NumFrames * 2; Scan += 1)
- {
- PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
- sizeof((TrackInfo->LocalSampleArray[Scan])));
- PRNGCHK(OutputData,&(OutputData[Scan]),sizeof(OutputData[Scan]));
- OutputData[Scan] += TrackInfo->LocalSampleArray[Scan];
- }
- }
- else
- {
- long Scan;
-
- for (Scan = 0; Scan < NumFrames; Scan += 1)
- {
- PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
- sizeof((TrackInfo->LocalSampleArray[Scan])));
- PRNGCHK(OutputData,&(OutputData[Scan]),sizeof(OutputData[Scan]));
- OutputData[Scan] += TrackInfo->LocalSampleArray[Scan];
- }
- }
- }
-
- /* update effects, but only AFTER they have been applied, so that parameters */
- /* come from the leading edge of an envelope period, rather than the */
- /* trailing edge. */
- TrackEffectIncrementDurationTimer(TrackInfo->EffectGenerator,NumDurationTicks);
-
- return True;
- }
-